import java.awt.image.*;
import java.io.*;
import java.awt.*;

import javax.swing.*;

import javax.imageio.*;

import com.sun.media.jai.codec.*;

import javax.swing.JFrame;

public class NewImage
{
	private ByteArrayOutputStream image;
	private String outputFile;
	private BufferedImage img;
	private BufferedImage[] images;
	private int[] sizes;
	private boolean[] formats;
	private int tiles, height, width, rows, columns;
	
	//Constructor for 4-Tile technique
	public NewImage(String filename, int tiles) throws IOException
	{
		image = new ByteArrayOutputStream();
		setTiles(tiles);
		loadImage(filename);
	}
	
	//Constructor for 8x8 blocks
	public NewImage(String filename) throws IOException
	{
		image = new ByteArrayOutputStream();
		loadImage(filename);
	}
	
	//Loads the image
	private void loadImage(String filename) throws IOException
	{
		img = ImageIO.read(new File(filename));
		height = img.getHeight();
		width = img.getWidth();
	}
	
	//Sets the output file
	public void setOutputFile(String filename)
	{
		outputFile = filename;
	}
	
	//Sets up variables for 4 tiles
	public void setTiles(int tiles)
	{
		this.tiles = tiles;
		sizes = new int[this.tiles];
		formats = new boolean[this.tiles];
	}
	
	//Sets up variables to use 8x8  blocks
	public void setTilesBlocks()
	{
		if(height % 8 == 0)
			rows = height / 8;
		else
			rows = height / 8 + 1;
		if(width % 8 == 0)
			columns = width / 8;
		else
			columns = width / 8 + 1;
		
		sizes = new int[rows * columns];
		formats = new boolean[rows * columns];
	}
	
	//Encode as JPG
	private ByteArrayOutputStream encodeJPEG(BufferedImage image) throws IOException
	{
		//Creates ByteArrayOutputStream for this specific image
		ByteArrayOutputStream jpg = new ByteArrayOutputStream();
		
		//Creates encoding parameter for JPEG format
		JPEGEncodeParam encodeParam = new JPEGEncodeParam();
		
		//Encodes image as JPEG file and stores in the ByteArrayOutputStream
		ImageEncoder encoder = ImageCodec.createImageEncoder("JPEG", jpg, encodeParam);
		encoder.encode(image.getRaster(), image.getColorModel());
		
		//Return ByteArrayOutputStream
		return jpg;
	}
	
	//Encode as PNG
	private ByteArrayOutputStream encodePNG(BufferedImage image) throws IOException
	{
		//Creates ByteArrayOutputStream for this specific image
		ByteArrayOutputStream png = new ByteArrayOutputStream();
		
		//Creates encoding parameter for JPEG format
		PNGEncodeParam encodeParam = PNGEncodeParam.getDefaultEncodeParam(image);
		
		//Encodes image as JPEG file and stores in the ByteArrayOutputStream
		ImageEncoder encoder = ImageCodec.createImageEncoder("PNG", png, encodeParam);
		encoder.encode(image.getRaster(), image.getColorModel());
		
		//Return ByteArrayOutputStream
		return png;
	}
	
	//Writes the image to the .SID file using 4-Tile technique
	public void writeToFileGeneral() throws IOException
	{
		//Creates file output stream for when image data is written
		FileOutputStream file = new FileOutputStream(outputFile);
		
		//setTiles();
				
		images = imageSplit();
		
		setTilesBlocks();
		System.out.println(rows);
		System.out.println(columns);
		
		//For loop to go through the 4 tiles
		//Encodes each tile as JPEG, PNG and compares sizes
		//Stores smaller image
		for(int i = 0; i < tiles; i++)
		{
			ByteArrayOutputStream jpg, png;
			
			jpg = encodeJPEG(images[i]);
			png = encodePNG(images[i]);
			
			if(jpg.size() <= png.size())
			{
				sizes[i] = jpg.size();
				formats[i] = false;
				image.write(jpg.toByteArray(), 0, sizes[i]);
			}
			else
			{
				sizes[i] = png.size();
				formats[i] = true;
				image.write(png.toByteArray(), 0, sizes[i]);
			}
			
			jpg.close();
			png.close();
		}
		
		image.writeTo(file);
		file.close();
	}
	
	//Writes the image to the .SID file using 8x8 blocks
	public void writeToFileGeneralBlocks() throws IOException
	{
		FileOutputStream file = new FileOutputStream(outputFile);
		
		setTilesBlocks();
		
		//Return the array of sub-images
		images = imageSplitBlocks();
		
		//Goes through the entire array of images
		//Encodes each sub-image as JPEG, PNG
		//Stores smaller one in .SID file
		for(int i = 0; i < rows * columns; i++)
		{
			ByteArrayOutputStream jpg, png;
			
			jpg = encodeJPEG(images[i]);
			png = encodePNG(images[i]);
			
			if(jpg.size() <= png.size())
			{
				sizes[i] = jpg.size();
				formats[i] = false;
				image.write(jpg.toByteArray(), 0, sizes[i]);
			}
			else
			{
				sizes[i] = png.size();
				formats[i] = true;
				image.write(png.toByteArray(), 0, sizes[i]);
			}
			
			jpg.close();
			png.close();
		}
		
		image.writeTo(file);
		file.close();
	}
	
	//Splits image into 4 tiles
	//Not the prettiest code
	private BufferedImage[] imageSplit() throws IOException
	{
		BufferedImage[] temp = new BufferedImage[tiles];
		int sqrt = (int) Math.sqrt(tiles);
		int xStart = 0, yStart = 0;
		
		for(int i = 0; i < sqrt; i++)
		{	
			xStart = 0;
			for(int j = 0; j < sqrt; j++)
			{
				temp[sqrt * i + j] = img.getSubimage(xStart, yStart, width / sqrt, height / sqrt);
				xStart += width / sqrt;
			}
			yStart += height / sqrt;
		}

		return temp;
	}
	
	//Splits image into 8x8 sub-images
	private BufferedImage[] imageSplitBlocks() throws IOException
	{
		BufferedImage[] temp = new BufferedImage[rows * columns];
		int count = 0;
		
		for(int i = 0; i < rows; i++)
			for(int j = 0; j < columns; j++)
			{
				int r = i * 8, c = j * 8;
				
				//Cases for near border of image
				if(c + 8 > width && r + 8 > height)
					temp[count] = img.getSubimage(j * 8, i * 8, width - c, height - r);
				else
				{
					if(c + 8 > width)
						temp[count] = img.getSubimage(j * 8, i * 8, width - c, 8);
					else if(r + 8 > height)
						temp[count] = img.getSubimage(j * 8, i * 8, 8, height - r);
				
					//all sub-images except ones near border
					else
						temp[count] = img.getSubimage(j * 8, i *8 , 8, 8);
				}
				count++;
			}
		
		return temp;
	}
	
	//Displays the image after 4-Tile technique
	public void displaySID() throws IOException
	{
		width = images[0].getWidth() + images[1].getWidth();
		height = images[0].getHeight() + images[2].getHeight();
		BufferedImage temp = new BufferedImage(width, height, 1);
		Graphics2D g = temp.createGraphics();
		Graphics2D newG = (Graphics2D) g.create(0, 0, width, height);
		
		FileInputStream s = new FileInputStream(outputFile);
		int length = 0;
		
		//Get total length of file
		for(int l = 0; l < tiles; l++)
			length += sizes[l];
		
		//Read in total file
		byte[] wholeimg = new byte[length];
		s.read(wholeimg);
		
		length = 0;
		
		//Byte array to hold just first sub-image
		byte[] tile = new byte[sizes[0]];
		
		//Copy data for first sub-image into byte array
		for(int c = 0; c < sizes[0]; c++)
			tile[c] = wholeimg[c];
		
		//Create image using byte array and draw to screen
		ImageIcon t = new ImageIcon(tile);
		newG.drawImage(t.getImage(), 0, 0, images[0].getWidth(), images[0].getHeight(), null);
		
		byte[] tile2 = new byte[sizes[1]];
		
		for(int c = 0; c < sizes[1]; c++)
			tile2[c] = wholeimg[c + sizes[0]];
		
		ImageIcon t2 = new ImageIcon(tile2);
		newG.drawImage(t2.getImage(), width / 2, 0, images[1].getWidth(), images[1].getHeight(), null);
		
		byte[] tile3 = new byte[sizes[2]];
		
		for(int c = 0; c < sizes[2]; c++)
			tile3[c] = wholeimg[c + sizes[0] + sizes[1]];
		
		ImageIcon t3 = new ImageIcon(tile3);
		newG.drawImage(t3.getImage(), 0, height / 2, images[2].getWidth(), images[2].getHeight(), null);
		
		byte[] tile4 = new byte[sizes[3]];
		
		for(int c = 0; c < sizes[3]; c++)
			tile4[c] = wholeimg[c + sizes[0] + sizes[1] + sizes[2]];
		
		ImageIcon t4 = new ImageIcon(tile4);
		newG.drawImage(t4.getImage(), width / 2, height / 2, images[3].getWidth(), images[3].getHeight(), null);
		
		ImageIcon dest = new ImageIcon(temp);
		JFrame frame = new JFrame("SID Image");
		JLabel label = new JLabel(dest);
		frame.add(label);
		frame.pack();
		frame.setVisible(true);
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	}
	
	//Displays the image using 8x8 technique
	public void displaySIDBlocks() throws IOException
	{
		BufferedImage temp = new BufferedImage(width, height, 1);
		Graphics2D g = temp.createGraphics();
		Graphics2D newG = (Graphics2D) g.create(0, 0, width, height);
		
		FileInputStream s = new FileInputStream(outputFile);
		int length = 0;
		
		//Get total length of file
		for(int l = 0; l < rows * columns; l++)
			length += sizes[l];
		
		//Read in total file
		byte[] wholeimg = new byte[length];
		s.read(wholeimg);
		
		length = 0;
		int count = 0;
		
		for(int i = 0; i < rows; i++)
 			for(int j = 0; j < columns; j++)
			{
 				byte[] tile = new byte[sizes[count]];
				
				for(int k = 0; k < sizes[count]; k++)
					tile[k] = wholeimg[length + k];
				
				length += sizes[count];
				
				//Create image using byte array and draw to screen
				ImageIcon t = new ImageIcon(tile);
				newG.drawImage(t.getImage(), 8 * j, 8 * i, 8, 8, null);
				count++;
			}
		
		ImageIcon dest = new ImageIcon(temp);
		JFrame frame = new JFrame("SID Image");
		JLabel label = new JLabel(dest);
		frame.add(label);
		frame.pack();
		frame.setVisible(true);
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	}
}